`
    场景内划分若干 Layout 使用上下左右连接网格式格局组成
    TLayout 由 Button的二维数组组成，代表行列，
    TButton 游戏内的按键 由包含cc.Button组件的节点组成
`

/**
 * 检查当前运行环境是否为原生环境，若是则重写错误处理函数
 */
if (cc.sys.isNative) {
    // 声明一个变量用于存储原有的错误处理函数
    let __handler: ((...args: any[]) => void) | undefined;
    // 若全局对象中已存在 '__errorHandler' 函数，则将其赋值给 __handler
    if (window['__errorHandler']) {
        __handler = window['__errorHandler'];
    }
    /**
     * 重写全局的错误处理函数，在原生环境下捕获游戏报错
     * @param {...any} args - 错误处理函数接收的参数
     */
    window['__errorHandler'] = function (...args: any[]) {
        // 打印游戏报错信息，表明是原生系统报错
        console.log('游戏报错,原生系统');
        // 调用原生 Java 方法，将错误信息传递给 Android 端
        jsb.reflection.callStaticMethod("org/cocos2dx/javascript/AppActivity", "onError", "(Ljava/lang/String;)V", JSON.stringify(args));
        // handleError(...args)
        // 若存在原有的错误处理函数，则调用它
        if (__handler) {
            __handler(...args);
        }
    }
}
const { property, ccclass } = cc._decorator;

/**
 * 定义方向编码的枚举类型，用于表示不同的按键方向
 */
export enum DirectionCode {
    LEFT,
    RIGHT,
    UP,
    DOWN,
    ENTER,
    BACK,
    OTHER
}


/**
 * 定义优先级级别的枚举类型，用于事件处理的优先级排序
 */
export enum PriorityLevel {
    BeforeSystem,
    SystemLevel,
    AfterSystem
}

/**
 * 根据传入的按键码返回对应的 DirectionCode 枚举值
 * @param directionCode - 传入的按键码
 * @returns 对应的 DirectionCode 枚举值
 */
let getDirectionCode = function (directionCode: number): DirectionCode {
    switch (directionCode) {
        case cc.macro.KEY.left:
        case cc.macro.KEY.dpadLeft:
            return DirectionCode.LEFT;
        case cc.macro.KEY.right:
        case cc.macro.KEY.dpadRight:
            return DirectionCode.RIGHT;
        case cc.macro.KEY.up:
        case cc.macro.KEY.dpadUp:
            return DirectionCode.UP;
        case cc.macro.KEY.down:
        case cc.macro.KEY.dpadDown:
            return DirectionCode.DOWN;
        case cc.macro.KEY.enter:
        case cc.macro.KEY.dpadCenter:
            return DirectionCode.ENTER;
        case cc.macro.KEY.back:
        case cc.macro.KEY.backspace:
        case cc.macro.KEY.num0:
        case 48:
            return DirectionCode.BACK;
        default:
            return DirectionCode.OTHER;
    }
}


/**
 * 获取某个节点在节点树中的渲染顺序
 * @param targetNode 目标节点，需要查询渲染顺序的节点
 * @returns 目标节点的渲染顺序，如果未找到则返回 -1
 */
let getNodeRenderOrder = function (targetNode: cc.Node): number {
    // 初始化渲染顺序为 0
    let renderOrder = 0;
    // 标记是否找到目标节点
    let found = false;
    // 获取场景中的 Canvas 节点
    const rootNode = cc.director.getScene().getChildByName('Canvas');

    // 若未找到 Canvas 节点，则返回 -1
    if (!rootNode) {
        return -1;
    }

    /**
     * 递归遍历节点树，查找目标节点并计算渲染顺序
     * @param node - 当前遍历的节点
     */
    function traverse(node: cc.Node) {
        // 若已找到目标节点，则停止遍历
        if (found) {
            return;
        }
        // 若当前节点为目标节点，则标记已找到
        if (node === targetNode) {
            found = true;
            return;
        }
        // 渲染顺序加 1
        renderOrder++;
        // 递归遍历当前节点的所有子节点
        for (let i = 0; i < node.children.length; i++) {
            traverse(node.children[i]);
        }
    }

    // 从 Canvas 节点开始遍历
    traverse(rootNode);

    // 若找到目标节点则返回渲染顺序，否则返回 -1
    return found ? renderOrder : -1;
}


/**
 * 定义位置信息的类型，包含行和列信息
 */
export type Site = {
    row: number,
    line: number,
}

/**
 * 定义手指移动信息的类型，包含起始和目标场景、布局、按钮等信息
 */
export type MoveInfo = {
    startScene: TScene;
    targetScene: TScene;
    startLayout: TLayout;
    targetLayout: TLayout;
    startBtn: TButton;
    targetBtn: TButton;
    isArrive: boolean; // 是否已抵达
    isExecuteeEvent: boolean; // 是否响应
};

/**
 * 定义按键事件参数的类型，包含方向编码、布局、按钮和双击信息
 * @param DirectionCode : 按键键值 
 * @param layout : 当前布局 
 * @param button : 当前按键 
 */
export type KeyEventParameters = {
    DirectionCode: DirectionCode | null
    layout: TLayout | null
    button: TButton
    doubleClick: boolean
};


/**
 * 定义场景状态的枚举类型，用于控制手指显示和按钮响应状态
 */
export enum SceneState {
    normal, // 显示手指并且正常响应按钮
    hideFinger, // 隐藏手指 正常响应按钮
    pause, // 显示手指，不响应按钮
    stop, // 不显示手指，不响应按钮
}


/**
 * 定义手指样式的枚举类型，包含手指和手两种样式
 */
export enum FingerStyle {
    finger = "finger",
    hand = "hand",
}


/**
 * 定义手动画名称的枚举类型，包含抓取、呼吸和松开三种动画
 */
export enum HandAnimaName {
    handCatch = "handCatch",
    handBreathing = "handBreathing",
    handLoosen = "handLoosen"
}


/**
 * 定义场景工具类，包含触发按钮事件、触摸移动按钮等方法
 */
export let SceneUtils = {
    /**
     * 在指定节点上触发触摸事件
     * @param node - 要触发事件的目标节点
     * @param eventType - 要触发的事件类型数组，默认为触摸开始和触摸结束事件
     * @param touchNode - 触摸的节点，默认为目标节点
     */
    emitButton: function (node: cc.Node, eventType: string[] = [cc.Node.EventType.TOUCH_START, cc.Node.EventType.TOUCH_END], touchNode?: cc.Node) {
        // 若未传入触摸节点，则使用目标节点
        touchNode = touchNode ? touchNode : node;
        // 获取触摸节点在世界坐标系中的位置
        let worldPoint = touchNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
        // 调用 emitButtonForPosition 方法触发事件
        SceneUtils.emitButtonForPosition(node, eventType, worldPoint);
    },

    /**
     * 针对指定节点，在指定的世界坐标位置触发一系列触摸事件。
     * @param node - 要触发事件的目标节点。
     * @param eventType - 要触发的事件类型数组，默认为触摸开始和触摸结束事件。
     * @param worldPoint - 触发事件的世界坐标位置。
     */
    emitButtonForPosition: function (node: cc.Node, eventType: string[] = [cc.Node.EventType.TOUCH_START, cc.Node.EventType.TOUCH_END], worldPoint: cc.Vec2) {
        // 创建一个新的触摸事件对象
        let event = new cc.Event.EventTouch([], false);
        // 创建一个新的触摸对象
        let touch = new cc.Touch();
        // 设置触摸对象的信息，包括触摸ID和世界坐标位置
        touch.setTouchInfo(0, worldPoint.x, worldPoint.y);
        // 设置事件的目标节点
        event.target = node;
        // 将触摸对象关联到事件中
        event.touch = touch;
        // 设置事件的当前目标节点
        event.currentTarget = node;
        // 遍历事件类型数组，依次触发每个事件
        for (let i = 0; i < eventType.length; i++) {
            // 触发节点上的指定事件，并传递事件对象
            event.type = eventType[i];
            node.activeInHierarchy && node.emit(eventType[i], event);
        }
    },

    /**
     * 在指定节点上模拟触摸移动操作
     * @param node - 要操作的目标节点
     * @param direciton - 移动的方向
     * @param offset - 移动的偏移量，默认为 100
     */
    touchMoveButton: function (node: cc.Node, direciton: DirectionCode, offset: number = 100) {
        // 创建一个新的触摸事件对象
        let event = new cc.Event.EventTouch([], false);
        // 创建一个新的触摸对象
        let touch = new cc.Touch();
        // 触摸的节点为目标节点
        let touchNode = node;
        // 获取触摸节点在世界坐标系中的位置
        let worldPoint = touchNode.convertToWorldSpaceAR(cc.Vec2.ZERO);
        // 设置触摸对象的信息，包括触摸ID和世界坐标位置
        touch.setTouchInfo(0, worldPoint.x, worldPoint.y);
        // 设置事件的目标节点
        event.target = node;
        // 将触摸对象关联到事件中
        event.touch = touch;
        // 设置事件类型为触摸开始
        event.type = cc.Node.EventType.TOUCH_START;
        // 设置事件的当前目标节点
        event.currentTarget = node;
        // 设置事件冒泡
        event.bubbles = true;
        // 触发触摸开始事件
        node.emit(cc.Node.EventType.TOUCH_START, event);
        // 根据不同的移动方向更新触摸位置
        switch (direciton) {
            case DirectionCode.LEFT:
                event.touch.setTouchInfo(0, worldPoint.x - offset, worldPoint.y);
                break;
            case DirectionCode.RIGHT:
                event.touch.setTouchInfo(0, worldPoint.x + offset, worldPoint.y);
                break;
            case DirectionCode.UP:
                event.touch.setTouchInfo(0, worldPoint.x, worldPoint.y + offset);
                break;
            case DirectionCode.DOWN:
                event.touch.setTouchInfo(0, worldPoint.x, worldPoint.y - offset);
                break;
        }
        // 设置事件类型为触摸移动
        event.type = cc.Node.EventType.TOUCH_MOVE;
        // 触发触摸移动事件
        node.emit(cc.Node.EventType.TOUCH_MOVE, event);
        // 设置事件类型为触摸结束
        event.type = cc.Node.EventType.TOUCH_END;
        // 触发触摸结束事件
        node.emit(cc.Node.EventType.TOUCH_END, event);
    }
}


/**
 * TButton 类代表游戏内的按钮，由包含 cc.Button 组件的节点组成，
 * 可以处理各种按键事件和手指移动事件，支持为不同事件添加回调函数。
 */
export class TButton {
    node: cc.Node;  // 包含 cc.Button 组件的节点
    offset: cc.Vec3; // 手指移动到按钮上的偏移位置

    monitor: Set<DirectionCode>; // 该按钮监控的方向按键集合
    keyDownEvents: ((paramenters: KeyEventParameters) => void)[][]; // 当按下按键后的回调，按优先级级别分组
    keyUpEvents: ((paramenters: KeyEventParameters) => void)[]; // 当抬起按键后的回调
    keyLongPressEvents: ((paramenters: KeyEventParameters, dt: number) => void)[]; // 当长按按键后的回调
    fingerMoveOutEvents: ((paramenters: MoveInfo | null) => void)[]; // 当手指从此按钮移开时的回调
    fingerAfterMoveInEvents: ((paramenters: MoveInfo | null) => void)[]; // 当手指移入此按钮之后的回调
    fingerbeforeMoveInEvents: ((paramenters: MoveInfo | null) => void)[]; // 当手指移入此按钮之前的回调
    parentLayout: TLayout; // 该按钮所属的布局

    /**
     * 构造函数，初始化 TButton 实例
     * @param node - 包含 cc.Button 组件的节点
     * @param monitor - 要监控的方向按键数组，默认为常见的方向和操作按键
     */
    constructor(node: cc.Node, monitor: DirectionCode[] = [DirectionCode.LEFT, DirectionCode.RIGHT, DirectionCode.UP, DirectionCode.DOWN, DirectionCode.ENTER, DirectionCode.BACK]) {
        if (!node) {
            cc.error("node 不能为null")
        }
        this.node = node;
        this.offset = cc.v3(0, 0);
        this.monitor = new Set(monitor);
        // 处理手指在节点上时，使用遥控器后的移动和 enter 操作
        this.addKeyDownEvent((paramenters: KeyEventParameters) => {
            let startButton: TButton = this,
                targetButton: TButton | null,
                context = SceneController.instance;
            switch (paramenters.DirectionCode) {
                case DirectionCode.LEFT:
                    if (this.monitor.has(DirectionCode.LEFT)) {
                        if (this.parentLayout.site.line > 0) {
                            // 在当前布局内左移按钮
                            this.parentLayout.site.line--;
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfoByChangeButton(this.parentLayout.parentScene, this.parentLayout, startButton, targetButton);
                        } else {
                            // 切换到左侧布局
                            this.parentLayout.parentScene.changeLayout(DirectionCode.LEFT);
                        }
                    }
                    break;
                case DirectionCode.RIGHT:
                    if (this.monitor.has(DirectionCode.RIGHT)) {
                        if (this.parentLayout.site.line < this.parentLayout.buttonList[this.parentLayout.site.row].length - 1) {
                            // 在当前布局内右移按钮
                            this.parentLayout.site.line++;
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfoByChangeButton(this.parentLayout.parentScene, this.parentLayout, startButton, targetButton);
                        } else {
                            // 切换到右侧布局
                            this.parentLayout.parentScene.changeLayout(DirectionCode.RIGHT);
                        }
                    }
                    break;
                case DirectionCode.UP:
                    if (this.monitor.has(DirectionCode.UP)) {
                        if (this.parentLayout.site.row > 0) {
                            // 在当前布局内上移按钮
                            this.parentLayout.site.row--;
                            this.parentLayout.site.line = Math.min(this.parentLayout.site.line, this.parentLayout.buttonList[this.parentLayout.site.row].length - 1)
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfoByChangeButton(this.parentLayout.parentScene, this.parentLayout, startButton, targetButton);
                        } else {
                            // 切换到上方布局
                            this.parentLayout.parentScene.changeLayout(DirectionCode.UP);
                        }
                    }
                    break;
                case DirectionCode.DOWN:
                    if (this.monitor.has(DirectionCode.DOWN)) {
                        if (this.parentLayout.site.row < this.parentLayout.buttonList.length - 1) {
                            // 在当前布局内下移按钮
                            this.parentLayout.site.row++;
                            this.parentLayout.site.line = Math.min(this.parentLayout.site.line, this.parentLayout.buttonList[this.parentLayout.site.row].length - 1)
                            targetButton = this.parentLayout.currButton;
                            context.setCurrentMoveInfoByChangeButton(this.parentLayout.parentScene, this.parentLayout, startButton, targetButton);
                        } else {
                            // 切换到下方布局
                            this.parentLayout.parentScene.changeLayout(DirectionCode.DOWN);
                        }
                    }
                    break;
                case DirectionCode.ENTER:
                    // 触发按钮事件
                    if (this.monitor.has(DirectionCode.ENTER)) {
                        SceneUtils.emitButton(this.node);
                    }
                    break;
                case DirectionCode.BACK:
                    if (this.monitor.has(DirectionCode.BACK)) {
                        // 获取返回操作的回调，优先使用布局的回调，其次使用场景的回调
                        let onback = this.parentLayout.onBack ? this.parentLayout.onBack : this.parentLayout.parentScene.onBack;
                        if (onback) {
                            if (onback instanceof cc.Node) {
                                // 若回调是节点，触发该节点的按钮事件
                                SceneUtils.emitButton(onback);
                            } else {
                                // 若回调是函数，执行该函数
                                onback();
                            }
                        }
                    }
                    break;
            }
        }, 0, PriorityLevel.SystemLevel);
    }

    /**
     * 为按钮添加缩放效果，手指移入时放大，移出时恢复原状
     * @param tbtn - 要添加效果的 TButton 实例
     * @param scale - 手指移入时按钮的缩放比例，默认为 1.05
     * @returns 添加效果后的 TButton 实例
     */
    public static addScaleEffect(tbtn: TButton, scale: number = 1.05) {
        tbtn.addAfterFingerInEvent((para: MoveInfo) => {
            // 手指移入后，将按钮缩放至指定比例
            cc.tween(para.targetBtn.node)
                .to(0.1, { scale: scale })
                .start();
        })
        tbtn.addFingerOutEvent((para: MoveInfo) => {
            // 手指移出后，将按钮缩放恢复至 1
            cc.tween(para.startBtn.node)
                .to(0.1, { scale: 1 })
                .start();
        })
        return tbtn;
    }

    /**
     * 清除按钮的所有属性和事件回调
     */
    clear() {
        this.node = null;
        this.offset = null;
        this.parentLayout = null;
        this.keyDownEvents = null;
        this.keyUpEvents = null
        this.keyDownEvents = null
        this.keyLongPressEvents = null
        this.fingerAfterMoveInEvents = null
        this.fingerMoveOutEvents = null
        this.fingerbeforeMoveInEvents = null
    }

    /**
     * 添加一个长按按键后的回调
     * @param event - 要添加的事件回调函数
     * @param priority - 事件优先级，越小越靠前，默认为最后
     */
    addKeyLongPressEvent(event: (paramenters: KeyEventParameters, dt: number) => void, priority?: number) {
        if (!this.keyLongPressEvents) this.keyLongPressEvents = [];
        if (priority == undefined || priority >= this.keyLongPressEvents.length) {
            priority = this.keyLongPressEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        // 按优先级插入事件回调
        this.keyLongPressEvents.splice(priority, 0, event);
    }

    /**
     * 添加一个按下按键后的回调
     * @param event - 要添加的事件回调函数
     * @param priorityIndex - 事件在对应优先级级别中的索引，默认为最后
     * @param priorityLevel - 事件优先级级别，默认为 AfterSystem
     */
    addKeyDownEvent(event: (paramenters: KeyEventParameters) => void, priorityIndex?: number, priorityLevel: PriorityLevel = PriorityLevel.AfterSystem) {
        if (!this.keyDownEvents) this.keyDownEvents = [[], [], []];
        if (priorityIndex == undefined || priorityIndex >= this.keyDownEvents[priorityLevel].length) {
            priorityIndex = this.keyDownEvents[priorityLevel].length;
        } else if (priorityIndex < 0) {
            priorityIndex = 0;
        }
        // 按优先级级别和索引插入事件回调
        this.keyDownEvents[priorityLevel].splice(priorityIndex, 0, event);
    }

    /**
     * 添加一个抬起按键后的回调
     * @param event - 要添加的事件回调函数
     * @param priority - 事件优先级，越小越靠前，默认为最后
     */
    addKeyUpEvent(event: (paramenters: KeyEventParameters) => void, priority?: number) {
        if (!this.keyUpEvents) this.keyUpEvents = [];
        if (priority == undefined || priority >= this.keyUpEvents.length) {
            priority = this.keyUpEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        // 按优先级插入事件回调
        this.keyUpEvents.splice(priority, 0, event);
    }

    /**
     * 添加一个手指移入按键后的回调
     * @param event - 要添加的事件回调函数
     * @param priority - 事件优先级，越小越靠前，默认为最后
     */
    addAfterFingerInEvent(event: (paramenters: MoveInfo | null) => void, priority?: number) {
        if (!this.fingerAfterMoveInEvents) this.fingerAfterMoveInEvents = [];
        if (priority == undefined || priority >= this.fingerAfterMoveInEvents.length) {
            priority = this.fingerAfterMoveInEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        // 按优先级插入事件回调
        this.fingerAfterMoveInEvents.splice(priority, 0, event);
    }

    /**
     * 添加一个手指移入按键前的回调
     * @param event - 要添加的事件回调函数
     * @param priority - 事件优先级，越小越靠前，默认为最后
     */
    addBeforeFingerInEvent(event: (paramenters: MoveInfo | null) => void, priority?: number) {
        if (!this.fingerbeforeMoveInEvents) this.fingerbeforeMoveInEvents = [];
        if (priority == undefined || priority >= this.fingerbeforeMoveInEvents.length) {
            priority = this.fingerbeforeMoveInEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        // 按优先级插入事件回调
        this.fingerbeforeMoveInEvents.splice(priority, 0, event);
    }

    /**
     * 添加一个手指移出按键后的回调
     * @param event - 要添加的事件回调函数
     * @param priority - 事件优先级，越小越靠前，默认为最后
     */
    addFingerOutEvent(event: (paramenters: MoveInfo | null) => void, priority?: number) {
        if (!this.fingerMoveOutEvents) this.fingerMoveOutEvents = [];
        if (priority == undefined || priority >= this.fingerMoveOutEvents.length) {
            priority = this.fingerMoveOutEvents.length;
        } else if (priority < 0) {
            priority = 0;
        }
        // 按优先级插入事件回调
        this.fingerMoveOutEvents.splice(priority, 0, event);
    }

    /**
     * 调用所有按下按键后的回调函数
     * @param DirectionCode - 按下的按键方向编码
     * @param layout - 当前布局
     * @param isDoubleClick - 是否为双击操作
     */
    callKeyDownEvents(DirectionCode: DirectionCode | null, layout: TLayout | null, isDoubleClick: boolean) {
        if (!this.keyDownEvents) return;
        let paramenters: KeyEventParameters = { DirectionCode: DirectionCode, layout: layout, button: this, doubleClick: isDoubleClick }
        // 遍历所有优先级级别的回调函数并执行
        this.keyDownEvents.forEach(level => level.forEach(item => item(paramenters)));
    }

    /**
     * 调用所有抬起按键后的回调函数
     * @param DirectionCode - 抬起的按键方向编码
     * @param layout - 当前布局
     */
    callKeyUpEvents(DirectionCode: DirectionCode, layout: TLayout | null) {
        if (!this.keyUpEvents) return;
        let paramenters: KeyEventParameters = { DirectionCode: DirectionCode, layout: layout, button: this, doubleClick: null }
        // 遍历所有回调函数并执行
        this.keyUpEvents.forEach(item => item(paramenters));
    }

    /**
     * 调用所有长按按键后的回调函数
     * @param DirectionCode - 长按的按键方向编码
     * @param layout - 当前布局
     * @param dt - 长按的时间间隔
     */
    callKeyLongPressEvents(DirectionCode: DirectionCode, layout: TLayout, dt: number) {
        if (!this.keyLongPressEvents) return;
        let paramenters: KeyEventParameters = { DirectionCode: DirectionCode, layout: layout, button: this, doubleClick: null }
        // 遍历所有回调函数并执行
        this.keyLongPressEvents.forEach(item => item(paramenters, dt));
    }

    /**
     * 调用所有手指移入按键后的回调函数
     * @param moveInfo - 手指移动信息
     */
    callfingerAfterMoveInEvents(moveInfo: MoveInfo) {
        if (!this.fingerAfterMoveInEvents) return;
        // 遍历所有回调函数并执行
        this.fingerAfterMoveInEvents.forEach(item => item(moveInfo));
    }

    /**
     * 调用所有手指移入按键前的回调函数
     * @param moveInfo - 手指移动信息
     */
    callfingerBeforeMoveInEvents(moveInfo: MoveInfo) {
        if (!this.fingerbeforeMoveInEvents) return;
        // 遍历所有回调函数并执行
        this.fingerbeforeMoveInEvents.forEach(item => item(moveInfo));
    }

    /**
     * 调用所有手指移出按键后的回调函数
     * @param moveInfo - 手指移动信息，可能为 null
     */
    callFingerMoveOutEvents(moveInfo: MoveInfo | null) {
        if (!this.fingerMoveOutEvents) return;
        // 遍历所有回调函数并执行
        this.fingerMoveOutEvents.forEach(item => item(moveInfo));
    }

}


export class TLayout {
    name: string; // Layout的名字
    site: Site; //当前选中的按键位置 [行,列]
    parentScene: TScene;
    onBack: (() => void)

    private _buttonList: TButton[][] | null = null; //按键
    public get buttonList(): TButton[][] | null {
        return this._buttonList;
    }
    public set buttonList(value: TButton[][] | null) {
        if (value) {
            if (value.length > 0 && value[0].length > 0) {
                if (this.buttonList) {
                    this.buttonList.forEach(row => row.forEach(but => but.clear()));
                }
                this._buttonList = value;
                value.forEach(row => row.forEach(but => but.parentLayout = this));
            } else {
                cc.error("Layout :" + this.name + "buttonList是空的！");
                return;
            }
        } else {
            this._buttonList = null;
            this.parentScene && this.parentScene.changeLayout(DirectionCode.OTHER);
        }
    }

    public get currButton(): TButton | null {
        if (this.buttonList) {
            return this.buttonList[this.site.row][this.site.line];
        } else {
            return null;
        }
    }

    public get currButtonRow(): TButton[] {
        if (this.buttonList) {
            return this.buttonList[this.site.row];
        } else {
            return null;
        }
    }

    constructor(LayoutName = "def") {
        this.name = LayoutName;
        this.site = { row: 0, line: 0 };
    }

    changeButton(dc: DirectionCode) {
        let targetBtn: TButton = null;
        let dire: any[], context = SceneController.instance;
        let oldbtn = this.currButton

        switch (dc) {
            case DirectionCode.LEFT:
                for (let i = this.site.line - 1; i >= 0; i--) {
                    if (this.buttonList[this.site.row][i]) {
                        this.site.line = i;
                        break;
                    }
                }
                break;
            case DirectionCode.RIGHT:
                for (let i = this.site.line + 1; i < this.buttonList[this.site.row].length; i++) {
                    if (this.buttonList[this.site.row][i]) {
                        this.site.line = i;
                        break;
                    }
                }
                break;
            case DirectionCode.UP:
                for (let i = this.site.row - 1; i >= 0; i--) {
                    if (this.buttonList[i][this.site.line]) {
                        this.site.row = i;
                        break;
                    }
                }
                break;
            case DirectionCode.DOWN:
                for (let i = this.site.row + 1; i <= this.buttonList.length; i++) {
                    if (this.buttonList[i][this.site.line]) {
                        this.site.row = i;
                        break;
                    }
                }
                break;
        }

        if (targetBtn) {
            context.setCurrentMoveInfoByChangeButton(this.parentScene, this, oldbtn, targetBtn);
            return true;
        } else {
            return false;
        }
    }

    clear() {
        this.name = null;
        this.site = null;
        this.parentScene = null;
        this.onBack = null;
        if (this.buttonList)
            for (let i = 0; i < this.buttonList.length; i++) {
                for (let j = 0; j < this.buttonList[i].length; j++) {
                    this.buttonList[i][j].clear();
                }
            }
    }
}

/**
 * TScene 类代表游戏中的一个场景，场景由多个 TLayout 布局组成，
 * 可以处理键盘事件、布局切换、手指样式更新等操作。
 */
export class TScene {
    /**
     * 场景节点
     */
    sceneNode: cc.Node;

    /**
     * 场景名称
     */
    sceneName: string;
    /**
     * 在场景中的渲染顺序
     */
    renderOrder: number;
    sceneState: SceneState;
    _fingerStyle: FingerStyle;
    onBack: (() => void) | cc.Node //当按下返回键时的回调 如果当前layout存在onBack，则不触发scene的onback
    site: Site; //当前选择的Layout
    private _layouts: TLayout[][]; //此Scene(场景)下的Layout(布局)

    _KeyDownTbtn: TButton;

    public get layouts(): TLayout[][] {
        return this._layouts;
    }
    public set layouts(value: TLayout[][]) {
        if (value) {
            if (this._layouts) this._layouts.forEach(row => row.forEach(item => item.clear()))
            this._layouts = value;
            this._layouts.forEach(row => row.forEach(item => item.parentScene = this))
        }
    }

    public get currLayout(): TLayout {
        return this.layouts[this.site.row][this.site.line];
    }

    public get isEmpty(): boolean {
        for (let i = 0; i < this.layouts.length; i++) {
            for (let j = 0; j < this.layouts[i].length; j++) {
                if (this.layouts[i][j]) {
                    return true;
                }
            }
        }
        return false;
    }

    get fingerStyle(): FingerStyle {
        return this._fingerStyle;
    }
    set fingerStyle(value: FingerStyle) {
        this._fingerStyle = value;
        SceneController.instance.updateFingerStyle();
    }

    /**
     * 创建Tscne 
     * @param sceneName scene名字
     */
    constructor(sceneNode: cc.Node, sceneState: SceneState = SceneState.normal) {
        this.sceneNode = sceneNode;
        this.sceneName = sceneNode.name;
        this.site = { row: 0, line: 0 };
        this.sceneState = sceneState
        this.layouts = [];
        this.fingerStyle = FingerStyle.finger;
    }

    /**
     * 根据传入的方向代码更改当前场景的布局
     * @param dc - 方向代码，为 DirectionCode 枚举类型
     * @returns 若成功找到并切换到有效布局返回 true，否则返回 false
     */
    changeLayout(dc: DirectionCode) {
        // 初始化目标布局为 null
        let targetLayout: TLayout = null;
        // 获取场景控制器实例
        const context = SceneController.instance;
        // 边界检查，确保当前布局存在，若不存在则直接返回 false
        if (!this.layouts || !this.layouts[this.site.row] || !this.layouts[this.site.row][this.site.line]) {
            return false;
        }
        // 获取当前布局作为旧布局
        const oldLayout = this.layouts[this.site.row][this.site.line];

        /**
         * 查找具有 buttonList 的布局
         * @param startIndex 起始索引，查找的起始位置
         * @param step 步长，每次查找移动的间隔
         * @param condition 循环条件函数，用于判断是否继续查找
         * @param getLayout 布局获取函数，根据索引获取对应的布局
         * @returns 若找到有效布局返回 true，否则返回 false
         */
        const findLayout = (
            startIndex: number,
            step: number,
            condition: (index: number) => boolean,
            getLayout: (index: number) => TLayout | undefined
        ): boolean => {
            // 从起始索引开始，根据条件循环查找
            for (let i = startIndex; condition(i); i += step) {
                // 获取当前索引对应的布局
                const layout = getLayout(i);
                // 若布局存在且包含 buttonList，则表示找到有效布局
                if (layout?.buttonList) {
                    return true;
                }
            }
            // 未找到有效布局
            return false;
        };

        /**
         * 处理上下方向的布局查找
         * @param startRow 起始行，查找的起始行号
         * @param step 行步长，每次查找移动的行数
         * @param condition 行循环条件函数，用于判断是否继续查找
         */
        const handleVerticalDirection = (
            startRow: number,
            step: number,
            condition: (row: number) => boolean
        ) => {
            // 从起始行开始，根据条件循环查找
            for (let row = startRow; condition(row); row += step) {
                // 检查当前行是否存在且有布局
                if (this.layouts[row] && this.layouts[row].length > 0) {
                    // 尝试使用当前列的布局
                    if (this.layouts[row][this.site.line]?.buttonList) {
                        // 若当前列布局有效，更新当前行号
                        this.site.row = row;
                        return;
                    }
                    // 若当前列布局无效，遍历当前行的所有列查找有效布局
                    for (let col = 0; col < this.layouts[row].length; col++) {
                        if (this.layouts[row][col]?.buttonList) {
                            // 若找到有效布局，更新当前行号和列号
                            this.site.row = row;
                            this.site.line = col;
                            return;
                        }
                    }
                }
            }
        };

        // 根据不同的方向代码执行不同的布局查找逻辑
        switch (dc) {
            case DirectionCode.LEFT:
                // 向左查找有效布局
                if (findLayout(
                    this.site.line - 1,
                    -1,
                    (col) => col >= 0,
                    (col) => this.layouts[this.site.row][col]
                )) {
                    // 若找到有效布局，更新当前列号
                    this.site.line--;
                }
                break;
            case DirectionCode.RIGHT:
                // 向右查找有效布局
                if (findLayout(
                    this.site.line + 1,
                    1,
                    (col) => col < this.layouts[this.site.row].length,
                    (col) => this.layouts[this.site.row][col]
                )) {
                    // 若找到有效布局，更新当前列号
                    this.site.line++;
                }
                break;
            case DirectionCode.UP:
                // 向上查找有效布局
                handleVerticalDirection(
                    this.site.row - 1,
                    -1,
                    (row) => row >= 0
                );
                break;
            case DirectionCode.DOWN:
                // 向下查找有效布局
                handleVerticalDirection(
                    this.site.row + 1,
                    1,
                    (row) => row < this.layouts.length
                );
                break;
            case DirectionCode.OTHER:
                // 标记是否找到有效布局
                let isOk = false;
                // 向上向左查找有效布局
                isOk = findLayout(
                    this.site.row,
                    -1,
                    (row) => row >= 0,
                    (row) => {
                        // 向左查找
                        for (let col = this.site.line; col >= 0; col--) {
                            if (this.layouts[row][col]?.buttonList) {
                                // 若找到有效布局，更新当前行号和列号
                                this.site.row = row;
                                this.site.line = col;
                                return this.layouts[row][col];
                            }
                        }
                        // 向右查找
                        for (let col = this.site.line + 1; col < this.layouts[row].length; col++) {
                            if (this.layouts[row][col]?.buttonList) {
                                // 若找到有效布局，更新当前行号和列号
                                this.site.row = row;
                                this.site.line = col;
                                return this.layouts[row][col];
                            }
                        }
                        // 未找到有效布局
                        return undefined;
                    }
                );

                if (!isOk) {
                    // 若未找到，向下查找有效布局
                    isOk = findLayout(
                        this.site.row + 1,
                        1,
                        (row) => row < this.layouts.length,
                        (row) => {
                            // 向左查找
                            for (let col = this.site.line; col >= 0; col--) {
                                if (this.layouts[row][col]?.buttonList) {
                                    // 若找到有效布局，更新当前行号和列号
                                    this.site.row = row;
                                    this.site.line = col;
                                    return this.layouts[row][col];
                                }
                            }
                            // 向右查找
                            for (let col = this.site.line + 1; col < this.layouts[row].length; col++) {
                                if (this.layouts[row][col]?.buttonList) {
                                    // 若找到有效布局，更新当前行号和列号
                                    this.site.row = row;
                                    this.site.line = col;
                                    return this.layouts[row][col];
                                }
                            }
                            // 未找到有效布局
                            return undefined;
                        }
                    );
                }

                if (!isOk) {
                    // 若仍未找到有效布局，打印错误信息
                    cc.error("没有了。。。");
                    // 将场景状态设置为停止
                    this.sceneState = SceneState.stop;
                    // 应用场景状态更改
                    this.apply();
                }
                break;
        }

        // 获取更新后的目标布局
        targetLayout = this.layouts[this.site.row]?.[this.site.line];
        if (targetLayout) {
            // 若当前场景为场景控制器中的栈顶场景
            if (SceneController.instance.scenes[SceneController.instance.scenes.length - 1] === this) {
                // 更新手指移动信息
                context.setCurrentMoveInfoByChangeLayout(
                    this,
                    oldLayout,
                    targetLayout,
                    oldLayout.currButton,
                    targetLayout.currButton
                );
            }
            // 成功找到并切换到有效布局
            return true;
        }
        // 未找到有效布局
        return false;
    }

    findLayoutByName(layoutName: string): TLayout {
        let findLayout: TLayout;
        A: for (let i = 0; i < this.layouts.length; i++) {
            for (let j = 0; j < this.layouts[i].length; j++) {
                if (this.layouts[i][j].name == layoutName) {
                    findLayout = this.layouts[i][j];
                    break A;
                }
            }
        }
        return findLayout;
    }

    /**
    * 在指定的位置添加一个Layout
    * @param layout 
    * @param site 
    */
    addLayout(layout: TLayout, site: Site = { row: 0, line: 0 }) {
        if (site.row > this.layouts.length) site.row = this.layouts.length;
        if (!this.layouts[site.row]) this.layouts[site.row] = [];
        if (this.layouts[site.row][site.line]) this.layouts[site.row][site.line].clear();
        this.layouts[site.row][site.line] = layout;
        this.layouts[site.row][site.line].parentScene = this;
    }

    onKeyDown(event: cc.Event.EventKeyboard, isDoubleClick: boolean) {
        if (this.sceneState == SceneState.pause || this.sceneState == SceneState.stop) return;
        let directionCode: DirectionCode = getDirectionCode(event.keyCode);
        this._KeyDownTbtn = this.currLayout.currButton;
        this.currLayout.currButton.callKeyDownEvents(directionCode, this.currLayout, isDoubleClick);
    }

    onKeyUp(event: cc.Event.EventKeyboard, context: SceneController) {
        if (this._KeyDownTbtn != this.currLayout.currButton) return;
        if (this.sceneState == SceneState.pause || this.sceneState == SceneState.stop) return;
        let code: DirectionCode = getDirectionCode(event.keyCode);
        if (code != null && this.currLayout && this.currLayout.currButton) {
            this.currLayout.currButton.callKeyUpEvents(code, this.currLayout);
        }
    }

    onKeyLongPress(DirectionCode: number, dt: number) {
        if (this._KeyDownTbtn != this.currLayout.currButton) return;
        if (this.sceneState == SceneState.pause || this.sceneState == SceneState.stop) return;
        if (this.currLayout && this.currLayout.currButton) {
            this.currLayout.currButton.callKeyLongPressEvents(DirectionCode, this.currLayout, dt);
        }
    }

    /**
     * 清除此Scene下的Layout
     */
    removeThis() {
        SceneController.instance.closeScene(this.sceneName);
        for (let i = 0; i < this.layouts.length; i++) {
            for (let j = 0; j < this.layouts[i].length; j++) {
                this.layouts[i][j].clear();
            }
        }
    }

    joinController() {
        SceneController.instance.openScene(this);
    }

    apply(isExecuteeEvent: boolean = false) {
        SceneController.instance.applyScene(this.sceneName, isExecuteeEvent);
    }
}
@ccclass
export default class SceneController extends cc.Component {

    scenes: TScene[] = [];

    recordDirectionCode: number = -1; //记录双击的键值

    _currentMoveInfo: MoveInfo | null = null; //手指的移动信息

    lastDirectionCode = []; //上一次按下的按键键值

    lastKeyDownSceneName; //上次按下按键时的SceneName

    handNode: cc.Node;
    fingerTip: cc.Node;


    public get topScene(): TScene {
        return this.scenes[this.scenes.length - 1]
    }

    public get fingerShow(): boolean {
        return this.finger.opacity > 0;
    }

    public set fingerShow(show: boolean) {
        if (show) {
            this.finger.opacity = 255;
            if (this.finger == this.fingerTip) {
                this.handNode.opacity = 0;
            } else {
                this.fingerTip.opacity = 0;
            }
        } else {
            this.handNode.opacity = 0;
            this.fingerTip.opacity = 0;
        }
    }

    public get finger(): cc.Node {
        let topScene = this.topScene;
        if (topScene) {
            let ret = null;
            switch (topScene.fingerStyle) {
                case FingerStyle.finger:
                    ret = this.fingerTip;
                    break;
                case FingerStyle.hand:
                    ret = this.handNode;
                    break;
            }
            return ret;
        } else {
            return this.fingerTip;
        }
    }

    /**
     * 表示即将离开旧按钮 去往新按钮
     */
    public set currentMoveInfo(v: MoveInfo | null) {
        this._currentMoveInfo = v;
        if (v && v.isExecuteeEvent) {
            if (this.currentMoveInfo) {
                if (this.currentMoveInfo.startBtn) {
                    this.currentMoveInfo.startBtn.callFingerMoveOutEvents(v);
                }
            }
            if (v) {
                if (v.targetBtn) {
                    v.targetBtn.callfingerBeforeMoveInEvents(v);
                }
            }
        }
    }

    public get currentMoveInfo(): MoveInfo | null {
        return this._currentMoveInfo;
    }

    public static getInstance(): SceneController {
        return this.instance;
    }

    static instance: SceneController = null;

    public _inService: boolean = true;


    public get inService(): boolean {
        return this._inService;
    }


    public set inService(v: boolean) {
        this._inService = v;
        this.lastDirectionCode = [];
    }


    onLoad() {
        SceneController.instance = this;
        cc.game.addPersistRootNode(this.node);

        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_DOWN, this._keyDown, this);
        cc.systemEvent.on(cc.SystemEvent.EventType.KEY_UP, this._keyUp, this);

        let fingerTip = new cc.Node(FingerStyle.finger);
        let finger = new cc.Node("finger").addComponent(cc.Sprite).node;
        let effect = new cc.Node("effect").addComponent(cc.Sprite).node;
        fingerTip.parent = this.node;
        fingerTip.position = cc.v3(700, 400);
        effect.parent = fingerTip;
        finger.parent = fingerTip;
        fingerTip.scale = 0.7;
        let fingerAnimation = fingerTip.addComponent(cc.Animation);
        cc.loader.loadRes("finger/finger", cc.SpriteAtlas, (err, spriteAtlas: cc.SpriteAtlas) => {
            let effects = spriteAtlas.getSpriteFrames();
            let fingerFrame = spriteAtlas.getSpriteFrame("finger");
            finger.getComponent(cc.Sprite).spriteFrame = fingerFrame;
            effects.length--;
            let clip = new cc.AnimationClip();
            clip["_duration"] = 1.25;
            clip.name = "fingerAnim";
            clip.sample = 16;
            clip.wrapMode = cc.WrapMode.Loop;
            clip.curveData = {
                paths: {
                    effect: {
                        comps: {
                            "cc.Sprite": {
                                spriteFrame: [{ frame: 0, value: effects[0] }, { frame: 0.25, value: effects[1] }
                                    , { frame: 0.5, value: effects[2] }, { frame: 0.75, value: effects[3] }, { frame: 1, value: effects[4] }]
                            }
                        }
                    }
                    , finger: { props: { position: [{ frame: 0, value: [45, -20, 0] }, { frame: 0.625, value: [45, -50, 0] }, { frame: 1.25, value: [45, -20, 0] },] } }
                }
            };
            fingerAnimation.addClip(clip);
            fingerAnimation.play(clip.name);
        })
        this.fingerTip = fingerTip;
        this.fingerTip.opacity = 0;
        cc.loader.loadRes("finger/hand", cc.Prefab, (err, hand: cc.Prefab) => {
            if (err) return;
            this.handNode = cc.instantiate(hand);
            this.handNode.opacity = 0;
            this.handNode.position = cc.v3(700, 400);
            this.handNode.parent = this.node;
        })

        cc.director.on(cc.Director.EVENT_BEFORE_DRAW, this.updateThis.bind(this));
    }

    /**
     * 增加场景时的设定MoveInfo 
     * @param oldScene 
     * @param newScene 
     */
    private setCurrentMoveInfoByChangeScene(oldScene: TScene | null, newScene: TScene) {
        if (newScene.currLayout.currButton) {
            if (oldScene) {
                this.currentMoveInfo = {
                    startScene: oldScene,
                    targetScene: newScene,
                    startLayout: oldScene.currLayout,
                    targetLayout: newScene.currLayout,
                    startBtn: oldScene.currLayout.currButton,
                    targetBtn: newScene.currLayout.currButton,
                    isArrive: false,
                    isExecuteeEvent: true
                }
            } else {
                this.currentMoveInfo = {
                    startScene: null,
                    targetScene: newScene,
                    startLayout: null,
                    targetLayout: newScene.currLayout,
                    startBtn: null,
                    targetBtn: newScene.currLayout.currButton,
                    isArrive: false,
                    isExecuteeEvent: true
                }
            }
            this.updateFingerSite(Infinity)
        } else {
            cc.log("scene" + newScene.sceneName + " targetNode null");
        }
    }

    /**
     * 同layout内按钮切换
     */
    setCurrentMoveInfoByChangeButton(scene: TScene, layout: TLayout, startButton: TButton | null, targetButton: TButton) {
        if (targetButton) {
            this.currentMoveInfo = { startScene: scene, targetScene: scene, startLayout: layout, targetLayout: layout, startBtn: startButton, targetBtn: targetButton, isArrive: false, isExecuteeEvent: true };
        } else {
            cc.log("scene" + scene.sceneName + " targetNode null");
        }
    }

    /**
     * 同Scene内按钮切换
     */
    setCurrentMoveInfoByChangeLayout(scene: TScene, startLayout: TLayout, targetLayout: TLayout, startButton: TButton | null, targetButton: TButton) {
        if (targetButton) {
            this.currentMoveInfo = { startScene: scene, targetScene: scene, startLayout: startLayout, targetLayout: targetLayout, startBtn: startButton, targetBtn: targetButton, isArrive: false, isExecuteeEvent: true };
        } else {
            cc.log("scene" + scene.sceneName + " targetNode null");
        }
    }

    updateFingerStyle() {
        let topScene = this.topScene;
        if (topScene) {
            this.fingerShow = topScene.sceneState == SceneState.normal || topScene.sceneState == SceneState.pause;
        }
    }

    /**
     * 打开scene,并且选择初始Layout 弹窗也是Scene
     * @param scene 
     * @param curSite 初始Layout的位置
     */
    public openScene(scene: TScene) {
        if (scene.layouts.length == 0) {
            return cc.error("scene: " + scene.sceneName + " 不存在Layout");
        };

        let exist = this.scenes.findIndex((item) => item.sceneName == scene.sceneName) >= 0;
        if (exist) {
            return cc.error("scene:" + scene.sceneName + "已存在");
        };

        let oldScene = this.scenes[this.scenes.length - 1];
        this.scenes.push(scene);
        this.scenes.forEach(item => item.renderOrder = getNodeRenderOrder(item.sceneNode));
        this.scenes.sort((a, b) => a.renderOrder - b.renderOrder);

        if (this.topScene == scene) {
            cc.log(scene.sceneName + " open");
        } else {
            cc.log(scene.sceneName + " insert " + this.scenes.indexOf(scene));
        }
        this.fingerShow = this.topScene.sceneState == SceneState.normal || this.topScene.sceneState == SceneState.pause;
        this.setCurrentMoveInfoByChangeScene(oldScene, this.topScene);
    }
    /**
     * 关闭Scene 手指移动到上一个scene
     * @param name 
     */
    public closeScene(closeName?: string) {
        let oldSceneIndex: number;
        if (closeName) {
            oldSceneIndex = this.scenes.findIndex(item => item.sceneName == closeName);
        } else {
            oldSceneIndex = this.scenes.length - 1;
        }
        if (oldSceneIndex < 0) return;
        //close的是不是栈顶的Scene 只有是栈顶的scene被close，才更新moveinfo和finger
        let isCloseTop: boolean = oldSceneIndex == this.scenes.length - 1;
        cc.log(this.scenes[oldSceneIndex].sceneName + " close");

        this.scenes.splice(oldSceneIndex, 1);

        this.lastDirectionCode = [];
        if (this.scenes.length == 0) {
            this.currentMoveInfo = null;
            this.fingerShow = false;
            return;
        }

        if (isCloseTop) {
            let newScene = this.scenes[this.scenes.length - 1];
            this.fingerShow = newScene.sceneState == SceneState.normal || newScene.sceneState == SceneState.pause;
            this.setCurrentMoveInfoByChangeScene(null, newScene);
        }
    }

    public getSceneByName(sceneName: string) {
        return this.scenes.find(itme => itme.sceneName == sceneName);
    }

    /**
     *  引用scene的修改
     * @param sceneName 
     */
    public applyScene(sceneName: string, isExecuteeEvent: boolean) {
        let sceneIndex: number = this.scenes.findIndex(itme => itme.sceneName == sceneName)
        let scene = this.scenes[sceneIndex];
        scene.layouts.forEach(row => row.forEach(layout => {
            if (layout.buttonList) {
                layout.site.row = Math.min(layout.site.row, layout.buttonList.length - 1);
                layout.site.line = Math.min(layout.buttonList[layout.site.row].length - 1, layout.site.line);
            }
        }))

        if (sceneIndex == this.scenes.length - 1) {
            this.fingerShow = scene.sceneState == SceneState.normal || scene.sceneState == SceneState.pause;
            this.currentMoveInfo = {
                startScene: this.currentMoveInfo.startScene,
                startLayout: this.currentMoveInfo.startLayout,
                startBtn: this.currentMoveInfo.startBtn,
                targetScene: scene,
                targetLayout: scene.currLayout,
                targetBtn: scene.currLayout.currButton,
                isArrive: false,
                isExecuteeEvent: isExecuteeEvent
            }
        }
    }

    removeHandAnim() {
        let anim = this.handNode.getComponent(cc.Animation);
        anim.stop();
        anim.clear();
    }


    playHandAnim(name: HandAnimaName, callBack?: () => void) {
        let anim = this.handNode.getComponent(cc.Animation);
        anim.off(cc.Animation.EventType.FINISHED);
        anim.play(name);
        callBack && anim.once(cc.Animation.EventType.FINISHED, callBack, this);
    }

    private _keyUp(event: cc.Event.EventKeyboard) {
        if (this.inService) {
            this.lastDirectionCode[event.keyCode] && (this.lastDirectionCode[event.keyCode] = 0);
            if (this.scenes.length > 0) {
                if (this.currentMoveInfo.targetScene.sceneName == this.lastKeyDownSceneName) {
                    this.scenes[this.scenes.length - 1].onKeyUp(event, this);
                }
            }
        }
    }

    private _keyDown(event: cc.Event.EventKeyboard) {
        if (this.inService) {
            if (this.scenes.length > 0) {
                if (!this.lastDirectionCode[event.keyCode]) {
                    this.lastDirectionCode[event.keyCode] = 1;

                    let isDoubleClick = false;
                    if (this.recordDirectionCode > 0) {
                        if (this.recordDirectionCode == event.keyCode) {
                            isDoubleClick = true;
                        }
                        this.recordDirectionCode = -1;
                    } else {
                        this.recordDirectionCode = event.keyCode;
                        this.scheduleOnce(() => this.recordDirectionCode = -1, 0.5);
                    }
                    this.lastKeyDownSceneName = this.currentMoveInfo.targetScene.sceneName;
                    this.scenes[this.scenes.length - 1].onKeyDown(event, isDoubleClick);
                }
            }
        }
    }

    private keyDownLongPress(dt: number) {
        for (let i = 0; i < this.lastDirectionCode.length; i++) {
            if (this.lastDirectionCode[i] == 1) {
                this.scenes[this.scenes.length - 1].onKeyLongPress(getDirectionCode(i), dt);
            }
        }
    }

    /**
     * 更新移动手指位置，抵达终点时调用终点的movein事件
     * @param dt 
     */
    private updateFingerSite(dt: number) {
        if (this.currentMoveInfo && this.currentMoveInfo.targetScene.sceneState !== SceneState.stop) {
            const targetBtn = this.currentMoveInfo.targetBtn;
            // 获取手指当前的世界坐标
            const selfPoint = this.finger.convertToWorldSpaceAR(cc.Vec3.ZERO);
            // 获取目标按钮的世界坐标并加上偏移量
            const targetPoint = targetBtn.node.convertToWorldSpaceAR(cc.Vec3.ZERO).add(targetBtn.offset);
            // 计算手指与目标按钮的距离
            const distance = targetPoint.sub(selfPoint).mag();
            // 根据距离计算移动速度，距离越远速度越快
            const moveSpeed = cc.v2(2000, 0).lerp(cc.v2(8000, 0), distance / 800).x;

            let arrive = distance < cc.director.getDeltaTime() * moveSpeed + 5;

            // 严谨检查 dt 是否为 Infinity
            if (!Number.isFinite(dt)) {
                arrive = true;
            }

            // 判断是否抵达目标按钮
            if (arrive) {
                if (!this.currentMoveInfo.isArrive && this.currentMoveInfo.isExecuteeEvent) {
                    // 触发手指移入目标按钮后的事件
                    targetBtn.callfingerAfterMoveInEvents(this.currentMoveInfo);
                }
                this.currentMoveInfo.isArrive = true;
                // 设置手指位置为目标按钮位置
                this.setFingerPosition(this.finger.parent.convertToNodeSpaceAR(targetPoint));
            } else {
                // 未抵达目标，更新手指位置
                const direction = targetPoint.sub(selfPoint).normalize();
                this.setFingerPosition(this.finger.position.add(direction.mul(moveSpeed * dt)));
            }
        }
    }

    private setFingerPosition(position: cc.Vec3) {
        this.fingerTip.position = position;
        this.handNode.position = position;
    }

    updateThis(dt: number) {
        if (this.inService) {
            this.updateFingerSite(dt);
            this.keyDownLongPress(dt);
        }
    }
}
